package com.ruoyi.business.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ruoyi.business.constants.Constant;
import com.ruoyi.business.domain.DataServer;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.RestTemplateUtil;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ruoyi.business.constants.CacheConstant.DATA_SERVER_TYPE;
import static com.ruoyi.business.constants.CacheConstant.SYNC_DATA_SERVER;

/**
 * @author liutao
 * @version 1.0
 * @description: TODO
 * @date 2024/3/26 19:49
 */
@Slf4j
@Service
public class CommonDataService {

    @Resource
    private ISysUserService userService;

    @Resource
    private ISysConfigService configService;

    @Resource
    private IDataServerService dataServerService;

    @Resource
    private ISlaveDataServerService slaveDataServerService;

    public List<Map<String, Object>> getDictData(String dictType) {
        List<SysDictData> list = DictUtils.getDictCache(dictType);
        List<Map<String, Object>> mapList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(list)) {
            for (SysDictData dictData : list) {
                Map<String, Object> map = Maps.newHashMap();
                map.put("value", dictData.getDictValue());
                map.put("name", dictData.getDictLabel());
                mapList.add(map);
            }
        }
        return mapList;
    }

    public List<Map<String, Object>> getUserList() {
        List<SysUser> list = userService.list();
        List<Map<String, Object>> result = Lists.newArrayList();
        if (CollUtil.isNotEmpty(list)) {
            for (SysUser user : list) {
                Map<String, Object> map = Maps.newHashMap();
                map.put("value", user.getUserId());
                map.put("name", user.getUserName());
                result.add(map);
            }
        }
        return result;
    }

    public String getArcgisTokens() {
        String url = configService.selectConfigByKey(Constant.ARCGIS_SERVER_URL) + "/arcgis/tokens/";
        Map<String, Object> params = Maps.newHashMap();
        params.put("request", "getToken");
        params.put("username", configService.selectConfigByKey(Constant.ARCGIS_USER_NAME));
        params.put("password", configService.selectConfigByKey(Constant.ARCGIS_PASSWORD));
        params.put("expiration", "60");
        params.put("f", "json");
//        HttpResponse httpResponse =  HttpRequest.post(url)
//                .contentType("application/x-www-form-urlencoded").formStr(params)
//                .timeout(10000)
//                .execute();
//        if (200 == httpResponse.getStatus()) {
//            JSONObject responseBody = JSONObject.parseObject(httpResponse.body());
//            String token = responseBody.getString("token");
//            RedisUtils.setCacheObject(Constant.ARCGIS_TOKEN, token, Duration.ofMinutes(1L));
//            return token;
//        }else{
//            log.error("获取token失败！");
//            throw new ServiceException("获取token失败！");
//        }
        MultiValueMap<String, Object> paramsMu = new LinkedMultiValueMap<>();
        paramsMu.set("request", "getToken");
        paramsMu.set("username", configService.selectConfigByKey(Constant.ARCGIS_USER_NAME));
        paramsMu.set("password", configService.selectConfigByKey(Constant.ARCGIS_PASSWORD));
        paramsMu.set("expiration", "60");
        paramsMu.set("f", "json");
        String responseBodyStr = RestTemplateUtil.postFromUrlencoded(url, paramsMu, null);
        log.info(">>>>>>responseBodyStr:{}", responseBodyStr);
        if (StrUtil.isEmpty(responseBodyStr)) {
            throw new ServiceException("获取token失败！");
        }
        JSONObject responseBody = JSONUtil.parseObj(responseBodyStr);
        log.info(">>>>>>responseBody:{}", responseBody);
        String token = responseBody.getStr("token");
        RedisUtils.setCacheObject(Constant.ARCGIS_TOKEN, token, Duration.ofMinutes(1L));
        return token;
    }

    @DSTransactional(rollbackFor = Exception.class)
    public void syncDataServer() {
        RLock lock = RedisUtils.getClient().getLock(SYNC_DATA_SERVER);
        try {
            // 尝试获取锁，等待时间 100ms，锁定时间 10秒
            if (lock.tryLock(100, TimeUnit.SECONDS)) {
                try {
                    List<DataServer> slaveList = slaveDataServerService.list();
                    if (CollUtil.isEmpty(slaveList)) {
                        return;
                    }
                    List<DataServer> masterList = dataServerService.list();
                    List<DataServer> resultList = Stream.concat(slaveList.stream(), masterList.stream())
                            .collect(Collectors.groupingBy(DataServer::getId, Collectors.counting()))
                            .entrySet().stream()
                            .filter(e -> e.getValue() == 1)
                            .map(Map.Entry::getKey)
                            .map(id -> Stream.concat(slaveList.stream(), masterList.stream()).filter(p -> p.getId().equals(id)).findFirst().get())
                            .collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(resultList)) {
                        if (dataServerService.saveBatch(resultList)) {
                            dataServerService.loadDataServerType();
                        }
                    }
                } catch (Exception e) {
                    throw new ServiceException("同步数据错误：", e);
                } finally {
                    //如果当前线程保持锁定则解锁
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            log.error("同步数据错误,获取锁失败：{}", e.getMessage());
        }
    }
}
